<!DOCTYPE html>
<html lang="zh-CN" data-theme="light">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <meta name="generator" content="VuePress 2.0.0-beta.53" />
    <meta name="theme" content="VuePress Theme Hope" />
    <link rel="icon" href="/doc/images/favicon.ico"><title>第 2 章 框架设计的核心要素 | AT 的文档库</title><meta name="description" content="At 的文档库，好记性不如烂笔头！">
    <style>
      :root {
        --bg-color: #fff;
      }

      html[data-theme="dark"] {
        --bg-color: #1d2025;
      }

      html,
      body {
        background: var(--bg-color);
      }
    </style>
    <script>
      const userMode = localStorage.getItem("vuepress-theme-hope-scheme");
      const systemDarkMode =
        window.matchMedia &&
        window.matchMedia("(prefers-color-scheme: dark)").matches;

      if (userMode === "dark" || (userMode !== "light" && systemDarkMode)) {
        document.querySelector("html").setAttribute("data-theme", "dark");
      }
    </script>
    <link rel="preload" href="/doc/assets/style.c3a4c10b.css" as="style" /><link rel="stylesheet" href="/doc/assets/style.c3a4c10b.css" />
    <link rel="modulepreload" href="/doc/assets/app.41440b49.js"><link rel="modulepreload" href="/doc/assets/第 2 章.html.ea8e67e2.js"><link rel="modulepreload" href="/doc/assets/_plugin-vue_export-helper.cdc0426e.js"><link rel="modulepreload" href="/doc/assets/第 2 章.html.38f55e7d.js"><link rel="prefetch" href="/doc/assets/index.html.0bcc895f.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.c8df2167.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue2.html.48e096c5.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue3.html.324e646c.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.fb0ad0e0.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章 导言.html.d6c662f8.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章 TypeScript 概述.html.e976154a.js" as="script" /><link rel="prefetch" href="/doc/assets/第 3 章 类型全解.html.e4f1b931.js" as="script" /><link rel="prefetch" href="/doc/assets/第 4 章 函数.html.701a2968.js" as="script" /><link rel="prefetch" href="/doc/assets/第 5 章 类和接口.html.8988d546.js" as="script" /><link rel="prefetch" href="/doc/assets/第 6 章 类型进阶.html.45b313e3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.a9dd6465.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.a3374cf8.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.c326eaaf.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.88582229.js" as="script" /><link rel="prefetch" href="/doc/assets/常见问题.html.e0f65550.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展使用.html.87cccc41.js" as="script" /><link rel="prefetch" href="/doc/assets/掌握 GIT.html.c150b234.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.a3c2858b.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.9a53d157.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.101afd82.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.8224e1cd.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.54b5ca2d.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.d839cc53.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.80e4d5cc.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.9a178a04.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展知识.html.3535ab30.js" as="script" /><link rel="prefetch" href="/doc/assets/项目搭建.html.e926587b.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.881e95b0.js" as="script" /><link rel="prefetch" href="/doc/assets/安装及使用.html.ba7314a3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.f9509ce0.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章.html.726a0bb9.js" as="script" /><link rel="prefetch" href="/doc/assets/Controllers.html.6b27764c.js" as="script" /><link rel="prefetch" href="/doc/assets/Module.html.3849b10b.js" as="script" /><link rel="prefetch" href="/doc/assets/Providers.html.7d2793f1.js" as="script" /><link rel="prefetch" href="/doc/assets/404.html.c5de7dd9.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.86b08b0b.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.f4119260.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue2.html.caf7eb18.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue3.html.6dc23cf4.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.868f2c80.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章 导言.html.9edf000e.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章 TypeScript 概述.html.76b27e4d.js" as="script" /><link rel="prefetch" href="/doc/assets/第 3 章 类型全解.html.9ad8a3d4.js" as="script" /><link rel="prefetch" href="/doc/assets/第 4 章 函数.html.9115df00.js" as="script" /><link rel="prefetch" href="/doc/assets/第 5 章 类和接口.html.e392a0d1.js" as="script" /><link rel="prefetch" href="/doc/assets/第 6 章 类型进阶.html.1b5b864e.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.e5a75968.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.974654cf.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.ed3fa7a3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.3b9ebcaa.js" as="script" /><link rel="prefetch" href="/doc/assets/常见问题.html.b427e39a.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展使用.html.76b3dd90.js" as="script" /><link rel="prefetch" href="/doc/assets/掌握 GIT.html.724416fc.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.8b55f9ef.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.8ee968a7.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.06371751.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.6b672044.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.b200eaff.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.5135d2c4.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.557b7bf5.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.7b535b09.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展知识.html.c6b233af.js" as="script" /><link rel="prefetch" href="/doc/assets/项目搭建.html.450f0b1d.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.6483ed06.js" as="script" /><link rel="prefetch" href="/doc/assets/安装及使用.html.b4d859f3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.f15cf3e9.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章.html.127b1e7c.js" as="script" /><link rel="prefetch" href="/doc/assets/Controllers.html.029df1e7.js" as="script" /><link rel="prefetch" href="/doc/assets/Module.html.f102db2a.js" as="script" /><link rel="prefetch" href="/doc/assets/Providers.html.9bef3753.js" as="script" /><link rel="prefetch" href="/doc/assets/404.html.2cfd6b20.js" as="script" /><link rel="prefetch" href="/doc/assets/photoswipe.esm.09e03fed.js" as="script" />
  </head>
  <body>
    <div id="app"><!--[--><!--[--><!--[--><span tabindex="-1"></span><a href="#main-content" class="skip-link sr-only">Skip to content</a><!--]--><div class="theme-container has-toc"><!--[--><!--[--><header class="navbar"><div class="navbar-left"><button class="toggle-sidebar-button" title="Toggle Sidebar"><span class="icon"></span></button><!--[--><!----><!--]--><a href="/doc/" class="brand"><!----><!----><span class="site-name">AT 的文档库</span></a><!--[--><!----><!--]--></div><div class="navbar-center"><!--[--><!----><!--]--><nav class="nav-links"><div class="nav-item hide-in-mobile"><a href="/doc/" class="nav-link" aria-label="首页"><!---->首页<!----></a></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="开发问题汇总"><span class="title"><!---->开发问题汇总</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/doc/development-question/Vue" class="nav-link" aria-label="Vue"><!---->Vue<!----></a></li></ul></button></div></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="系统笔记"><span class="title"><!---->系统笔记</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/doc/sysnotes/Git/%E6%8E%8C%E6%8F%A1%20GIT.html" class="nav-link" aria-label="GIT"><!---->GIT<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/Nvm/%E5%AE%89%E8%A3%85%E5%8F%8A%E4%BD%BF%E7%94%A8.html" class="nav-link" aria-label="NVM"><!---->NVM<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/Linux/%E5%9F%BA%E7%A1%80%E6%8E%8C%E6%8F%A1.html" class="nav-link" aria-label="Linux"><!---->Linux<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/JavaScript" class="nav-link" aria-label="JavaScript"><!---->JavaScript<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/CSS" class="nav-link" aria-label="CSS"><!---->CSS<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/HTML" class="nav-link" aria-label="HTML"><!---->HTML<!----></a></li></ul></button></div></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="阅读笔记"><span class="title"><!---->阅读笔记</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/doc/readbook/《Vue.js 设计与实现》/" class="nav-link" aria-label="《Vue.js 设计与实现》"><!---->《Vue.js 设计与实现》<!----></a></li><li class="dropdown-item"><a href="/doc/readbook/《TypeScript 编程》/" class="nav-link" aria-label="《TypeScript 编程》"><!---->《TypeScript 编程》<!----></a></li></ul></button></div></div></nav><!--[--><!----><!--]--></div><div class="navbar-right"><!--[--><!----><!--]--><div class="nav-item hide-in-mobile"><button id="appearance-switch"><svg xmlns="http://www.w3.org/2000/svg" class="icon auto-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="auto icon" style="display:block;"><path d="M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon dark-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="dark icon" style="display:none;"><path d="M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon light-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="light icon" style="display:none;"><path d="M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"></path></svg></button></div><form class="search-box" role="search"><input type="search" autocomplete="off" spellcheck="false" value><!----></form><!--[--><!----><!--]--><button class="toggle-navbar-button" aria-label="Toggle Navbar" aria-expanded="false" aria-controls="nav-screen"><span class="button-container"><span class="button-top"></span><span class="button-middle"></span><span class="button-bottom"></span></span></button></div></header><!----><!--]--><!----><div class="toggle-sidebar-wrapper"><span class="arrow left"></span></div><aside class="sidebar"><!--[--><!----><!--]--><ul class="sidebar-links"><li><section class="sidebar-group"><button class="sidebar-heading clickable"><!----><span class="title">《TypeScript 编程》</span><span class="arrow right"></span></button><!----></section></li><li><section class="sidebar-group"><button class="sidebar-heading clickable active"><!----><span class="title">《Vue.js 设计与实现》</span><span class="arrow down"></span></button><ul class="sidebar-links"><li><section class="sidebar-group"><button class="sidebar-heading clickable active"><!----><span class="title">第一篇 框架设计概览</span><span class="arrow down"></span></button><ul class="sidebar-links"><li><!--[--><a href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%201%20%E7%AB%A0.html" class="nav-link sidebar-link sidebar-page" aria-label="第 1 章 权衡的艺术"><!---->第 1 章 权衡的艺术<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html" class="router-link-active router-link-exact-active nav-link active sidebar-link sidebar-page active" aria-label="第 2 章 框架设计的核心要素"><!---->第 2 章 框架设计的核心要素<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-1-提升用户的开发体验" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2.1 提升用户的开发体验"><!---->2.1 提升用户的开发体验<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-2-控制框架的代码体积" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2.2 控制框架的代码体积"><!---->2.2 控制框架的代码体积<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-3-框架要做到良好的-tree-shaking" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2.3 框架要做到良好的 Tree-Shaking"><!---->2.3 框架要做到良好的 Tree-Shaking<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-4-框架应该输出增怎样的构建产物" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2.4 框架应该输出增怎样的构建产物"><!---->2.4 框架应该输出增怎样的构建产物<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-5-特性开关" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2.5 特性开关"><!---->2.5 特性开关<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-6-错误处理" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2.6 错误处理"><!---->2.6 错误处理<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-7-良好的-ts-类型支持" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2.7 良好的 TS 类型支持"><!---->2.7 良好的 TS 类型支持<!----></a><ul class="sidebar-sub-headers"></ul></li></ul><!--]--></li></ul></section></li></ul></section></li></ul><!--[--><!----><!--]--></aside><!--[--><main class="page" id="main-content"><!--[--><!----><nav class="breadcrumb disable"></nav><div class="page-title"><h1><!---->第 2 章 框架设计的核心要素</h1><div class="page-info"><!----><!----><!----><!----><!----><span class="reading-time-info" aria-label="阅读时间⌛" data-balloon-pos="down"><svg xmlns="http://www.w3.org/2000/svg" class="icon timer-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="timer icon"><path d="M799.387 122.15c4.402-2.978 7.38-7.897 7.38-13.463v-1.165c0-8.933-7.38-16.312-16.312-16.312H256.33c-8.933 0-16.311 7.38-16.311 16.312v1.165c0 5.825 2.977 10.874 7.637 13.592 4.143 194.44 97.22 354.963 220.201 392.763-122.204 37.542-214.893 196.511-220.2 389.397-4.661 5.049-7.638 11.651-7.638 19.03v5.825h566.49v-5.825c0-7.379-2.849-13.981-7.509-18.9-5.049-193.016-97.867-351.985-220.2-389.527 123.24-37.67 216.446-198.453 220.588-392.892zM531.16 450.445v352.632c117.674 1.553 211.787 40.778 211.787 88.676H304.097c0-48.286 95.149-87.382 213.728-88.676V450.445c-93.077-3.107-167.901-81.297-167.901-177.093 0-8.803 6.99-15.793 15.793-15.793 8.803 0 15.794 6.99 15.794 15.793 0 80.261 63.69 145.635 142.01 145.635s142.011-65.374 142.011-145.635c0-8.803 6.99-15.793 15.794-15.793s15.793 6.99 15.793 15.793c0 95.019-73.789 172.82-165.96 177.093z"></path></svg><span>大约 7 分钟</span><meta property="timeRequired" content="PT7M"></span></div><hr></div><div class="toc-place-holder"><aside id="toc"><div class="toc-header">此页内容</div><div class="toc-wrapper"><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-1-提升用户的开发体验" class="router-link-active router-link-exact-active toc-link level2">2.1 提升用户的开发体验</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-2-控制框架的代码体积" class="router-link-active router-link-exact-active toc-link level2">2.2 控制框架的代码体积</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-3-框架要做到良好的-tree-shaking" class="router-link-active router-link-exact-active toc-link level2">2.3 框架要做到良好的 Tree-Shaking</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-4-框架应该输出增怎样的构建产物" class="router-link-active router-link-exact-active toc-link level2">2.4 框架应该输出增怎样的构建产物</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-5-特性开关" class="router-link-active router-link-exact-active toc-link level2">2.5 特性开关</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-6-错误处理" class="router-link-active router-link-exact-active toc-link level2">2.6 错误处理</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html#_2-7-良好的-ts-类型支持" class="router-link-active router-link-exact-active toc-link level2">2.7 良好的 TS 类型支持</a></li><!----><!--]--></ul></div></aside></div><!----><div class="theme-hope-content"><h1 id="第-2-章-框架设计的核心要素" tabindex="-1"><a class="header-anchor" href="#第-2-章-框架设计的核心要素" aria-hidden="true">#</a> 第 2 章 框架设计的核心要素</h1><h2 id="_2-1-提升用户的开发体验" tabindex="-1"><a class="header-anchor" href="#_2-1-提升用户的开发体验" aria-hidden="true">#</a> 2.1 提升用户的开发体验</h2><p>衡量框架是否足够优秀的指标之一就是看它的开发体验如何。Vue.js 3 中:</p><p><code>createApp(App).mount(&#39;#empty&#39;)</code></p><p>当创建应用时并试图将其挂载到一个不存在的 DOM 节点时，会收到一条警告信息。</p><p>当我们在控制台打印 ref 数据时：</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token keyword">const</span> num <span class="token operator">=</span> <span class="token function">ref</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>num<span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div></div></div><p>输出结果如图：</p><p><img src="/doc/assets/2.1-1.90f373b0.jpg" alt="2.1-1"></p><p>打印的数据不是非常直观。可以再打印的时候选则打印 <code>num.value</code> 。 也可以在浏览器开启自定义格式化。Vue.js 3 内部有 initCustomFormatter 的函数。 该函数就是用来开发环境下初始化自定义 formatter 的。以 Chrome 为例： 打开 DevTools 的设置，然后勾选 Enable custom formatter。</p><p><img src="/doc/assets/2.1-2.ad522e94.jpg" alt="2.1-2"></p><p>打印结果为 <code>Ref&lt;0&gt;</code> ，可以很直观的看出打印内容。</p><h2 id="_2-2-控制框架的代码体积" tabindex="-1"><a class="header-anchor" href="#_2-2-控制框架的代码体积" aria-hidden="true">#</a> 2.2 控制框架的代码体积</h2><p>框架大小也是衡量框架的标准之一。在实现相同功能下，代码越少越好。</p><p>Vue.js 3 源码中，每一个 warn 函数的调用都会配合 __DEV__ 常量的检查：</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token keyword">if</span><span class="token punctuation">(</span>__DEV__ <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>res<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token function">warn</span><span class="token punctuation">(</span><span class="token comment">/* ... */</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>如上代码，打印警告信息的前提 __DEV__ 必须为 <strong>true</strong>。 Vue.js 使用 rollup.js 构建，__DEV__ 常量是通过 rollup.js 的插件配置定义。功能类似于 webpack 的 DefinePlugin 插件。</p><p>Vue.js 在输出时，会输出两个版本，一个用于开发环境（vue.global.js）,另一个用于生产环境（vue.global.prod.js）。</p><p>在开发环境时常量 __DEV__ 为 true，所以警告信息在有信息的情况下会始终提示。</p><p>生产环境中，常量 __DEV__ 为 false，所以在线上看不到平时开发中的一个 waring 信息。</p><p>这样<strong>在开发环境中为用户提供友好的警告信息的同时，不会增加生产环境的代码。</strong></p><h2 id="_2-3-框架要做到良好的-tree-shaking" tabindex="-1"><a class="header-anchor" href="#_2-3-框架要做到良好的-tree-shaking" aria-hidden="true">#</a> 2.3 框架要做到良好的 Tree-Shaking</h2><p>Tree-Shaking 指的就是消除那些永远不被执行的代码，也就是排除 dead code。</p><p>想要实现 Tree-Shaking，必须满足使用 ES(ES Module)，因为 Tree-Shaking 依赖 ESM 的静态结构。</p><p>如果一个函数调用会产生<strong>副作用</strong>，那么就不会被 Tree-Shaking 移除。副作用就是，当调用函数时会对外部产生影响，例如修改全局变量。</p><p>一些构建工具都会提供一个机制，可以明确的支出这段代码不会产生副作用。</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token keyword">import</span><span class="token punctuation">{</span>formatt<span class="token punctuation">}</span> <span class="token string">&#39;./utils.js&#39;</span>

<span class="token comment">/*#__PURE__*/</span> <span class="token function">formatt</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>注释代码 <code>/*#__PURE__*/</code> 作用就是告诉 rollup.js，对于函数的调用不会产生副作用。不只是 rollup 会识别，webpack 也会识别。</p><p>这样会不会产生心智负担呢？答案是不会，因为通常产生副作用的代码都是模块内函数的顶级调用。</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 顶级调用</span>

<span class="token keyword">function</span> <span class="token function">bar</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 函数内部调用</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码可以看出，顶级调用可能会产生副作用，而函数内部调用时，只要 bar 函数没有调用，内部的 foo 函数就不会执行。因此不会产生副作用。</p><h2 id="_2-4-框架应该输出增怎样的构建产物" tabindex="-1"><a class="header-anchor" href="#_2-4-框架应该输出增怎样的构建产物" aria-hidden="true">#</a> 2.4 框架应该输出增怎样的构建产物</h2><p>上边提到 Vue.js 会为开发环境和生产环境输出不同的包。构建产物除环境上的区分外还会根据不同使用场景输出其他形式的产物。</p><p>Vue.js 根据不同需求背景构建不同类型的产物。</p><p>首先，Vue.js 希望用户可以直接使用 script 标签引入框架并使用。</p><p>所以框架输出一种叫做 IIFE 格式的资源。<strong>IIFE</strong> (Immediately Invoked Function Expression)，即”<strong>立即调用的函数表达式</strong>“。</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>

<span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>vue.global.js 文件就是 IIFE 形式的资源，大致结构如下：</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token keyword">var</span> Vue <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">exports</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  <span class="token comment">// ......</span>
  exports<span class="token punctuation">.</span>createApp <span class="token operator">=</span> createApp
  <span class="token comment">// ......</span>
  <span class="token keyword">return</span> exports
<span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>在 rollup.js 中，可以通过配置 <code>format:&#39;iife&#39;</code> 来输出这种形式的资源：</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token comment">// rollup.config.js</span>
<span class="token keyword">const</span> config <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">input</span><span class="token operator">:</span><span class="token string">&#39;input.js&#39;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">output</span><span class="token operator">:</span><span class="token punctuation">{</span>
    <span class="token literal-property property">file</span><span class="token operator">:</span><span class="token string">&#39;output.js&#39;</span><span class="token punctuation">,</span>
    <span class="token literal-property property">format</span><span class="token operator">:</span><span class="token string">&#39;iife&#39;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> config
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>伴随着技术发展和浏览器支持，现在主流浏览器都可以支持原生 ESM。所以用户除了使用 script 标签引用 IIFE 资源外，还可以直接引入 ESM 格式资源。</p><p>Vue.js 3 还会输出 vue.esm.browser.js 文件，用户可以直接用 <code>&lt;script type=&quot;module&quot; sec=&quot;vue.esm.browser.js&quot;&gt;&lt;/script&gt;</code> 引入。</p><p>rollup.js 中，通过配置 <code>format:&#39;ems&#39;</code> 来输出。</p><p>Vue.js 会输出带有 -browser 和 -bundler 的资源文件。无论是 rollup 还是 webpack 在寻找资源时，如果 package.json 中存在 module 字段，都会优先 使用 modlue 字段指向的资源代替 main 字段指向的资源。</p><p>Vue.js 源码中：</p><div class="language-json line-numbers-mode" data-ext="json"><pre class="language-json"><code><span class="token comment">// packages/vue/package.json</span>
<span class="token punctuation">{</span>
  <span class="token property">&quot;main&quot;</span><span class="token operator">:</span><span class="token string">&quot;index.js&quot;</span><span class="token punctuation">,</span>
  <span class="token property">&quot;module&quot;</span><span class="token operator">:</span><span class="token string">&quot;dist/vue.runtime.esm-bundler.js&quot;</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>其中 module 字段指向的资源，意思是说，如果项目使用的 webpack 构建的，那么使用的 Vue.js 资源就是 vue.runtime.esm-bundler.js。也就是说带有 -bundler 字样的 ESM 资源文件是给打包工具使用的。而带有 -browser 字样的资源文件是直接给 script 使用的。</p><p>两者之间的区别：上边提到过 __DEV__ 常量。当构建 bowser 资源文件时， 如果是开发环境，那么 __DEV__ 会设置为 true；如果是生产环境会被设置为 false，从而呗 Tree-Shaking 删除。当构建 bundler 资源文件时，不能直接把 __DEV__ 设置为 true 或 false，而使用的是 <code>p<wbr>rocess.env.NODE_ENV !== &#39;production&#39;</code> 替换 __DEV__ 。</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token keyword">if</span><span class="token punctuation">(</span>__DEV__<span class="token punctuation">)</span><span class="token punctuation">{</span>
  <span class="token function">warn</span><span class="token punctuation">(</span><span class="token comment">/* ...... */</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">if</span><span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span><span class="token operator">!==</span><span class="token string">&#39;production&#39;</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  <span class="token function">warn</span><span class="token punctuation">(</span><span class="token comment">/* ...... */</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>好处是用户可以通过 webpack 配置自行决定构建资源的目标环境，但是最终效果是一样的。这段代码只会出现在开发环境中。</p><p>用户除了可以直接使用 script 标签引入外，还可以使用 require 语句引用。当进行服务端渲染时，Vue.js 的代码是在 Node.js 环境中运行，而非浏览器。Node.js 资源模块格式应该是 CommonJs，简称 cjs。</p><p>配置 rollup.config.js 中的 <code>format:&#39;cjs&#39;</code> 即可构建 cjs 形式资源。</p><h2 id="_2-5-特性开关" tabindex="-1"><a class="header-anchor" href="#_2-5-特性开关" aria-hidden="true">#</a> 2.5 特性开关</h2><p>Vue.js 提供了特性开关，用户可以通过设置特性的开关为 true 或 false 来代表或开启对应的特性：</p><ul><li>对于用户关闭的特性，可以利用 Tree-Shaking 机制让其不包含在最终的资源中</li><li>该机制尾矿嘉设计带来了灵活性，可以通过特性开关人以为框架添加新的特性，而不用担心资源体积变大。同时，框架升级也可以通过特性开关来支持遗留 API。</li></ul><p>特性开关的本质就是一个常量，例如：_<em>VUE_OPTIONS_API_</em>，可以使用 webpack.DefinePlugin 插件实现开关的打开或关闭</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token keyword">new</span> <span class="token class-name">webpack<span class="token punctuation">.</span>DefindPlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token literal-property property">_<wbr>_VUE_OPTIONS_API__</span><span class="token operator">:</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>_<em>VUE_OPTIONS_API_</em> 控制是为了在 Vue.js 3 中可以兼容 Vue.js 2 的 options api。如果明确不使用 options api 的话可以关闭，最终打包的资源文件会减小体积。</p><h2 id="_2-6-错误处理" tabindex="-1"><a class="header-anchor" href="#_2-6-错误处理" aria-hidden="true">#</a> 2.6 错误处理</h2><p>错误处理是框架开发过程中非常重要的环节。错误处理机制的好坏直接决定了用户应用程序的健壮性，还决定了用户开发时处理错误的心智负担。</p><p>封装统一的错误处理接口，大大减少用户频繁使用 try...catch。</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token comment">// utils.js</span>
<span class="token keyword">let</span> handlerError <span class="token operator">=</span> <span class="token keyword">null</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token function">foo</span><span class="token punctuation">(</span><span class="token parameter">fn</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">callWithErrorHandling</span><span class="token punctuation">(</span>fn<span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
  
  <span class="token function">registerErrorHandler</span><span class="token punctuation">(</span><span class="token parameter">fn</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    handlerError <span class="token operator">=</span> fn
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token function">callWithErrorHandling</span><span class="token punctuation">(</span><span class="token parameter">fn</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">try</span><span class="token punctuation">{</span>
    fn<span class="token operator">&amp;&amp;</span><span class="token function">fn</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">catch</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">{</span>
    <span class="token function">handlerError</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span>
  <span class="token punctuation">}</span>

<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>提供 registerErrorHandler 函数，用户可以将其注册为错误处理程序，然后在 callWithErrorHandling 函数内部捕获错误后，把错误信息传递给注册的错误处理程序。</p><p>用户在使用时：</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token keyword">import</span> utils <span class="token keyword">from</span> <span class="token string">&#39;utils.js&#39;</span>

utils<span class="token punctuation">.</span><span class="token function">registerErrorHandler</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token operator">=&gt;</span><span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

utils<span class="token punctuation">.</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">=&gt;</span><span class="token punctuation">{</span>
  <span class="token comment">// ...</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上边就是 Vue.js 错误处理的原理，可以在源码中搜索到 callWithErrorHandler 函数。Vue.js 中也可以注册统一错误处理函数。</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token keyword">import</span> App <span class="token keyword">from</span> <span class="token string">&#39;./App.vue&#39;</span>

<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">createApp</span><span class="token punctuation">(</span>App<span class="token punctuation">)</span>
app<span class="token punctuation">.</span>config<span class="token punctuation">.</span><span class="token function-variable function">errorHandler</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> instance<span class="token punctuation">,</span> info</span><span class="token punctuation">)</span><span class="token operator">=&gt;</span><span class="token punctuation">{</span>

<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>错误处理器接收三个参数：错误对象、触发该错误的组件实例和一个指出错误来源类型信息的字符串。</p><p>它可以从下面这些来源中捕获错误：</p><ul><li>组件渲染器</li><li>事件处理器</li><li>生命周期钩子</li><li>setup() 函数</li><li>侦听器</li><li>自定义指令钩子</li><li>过渡 (Transition) 钩子</li></ul><h2 id="_2-7-良好的-ts-类型支持" tabindex="-1"><a class="header-anchor" href="#_2-7-良好的-ts-类型支持" aria-hidden="true">#</a> 2.7 良好的 TS 类型支持</h2><p>TS 是微软推出的一款开源编程语言，是 JS 的超集，能够为 JS 提供良好的类型支持。</p><p>使用 TS 的好处有：</p><ul><li>代码即文档</li><li>编辑器代码提示</li><li>一定程度上可以避免低级 BUG</li><li>代码可维护性更强。</li></ul></div><!----><footer class="page-meta"><!----><!----><!----></footer><nav class="page-nav"><a href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%201%20%E7%AB%A0.html" class="nav-link prev" aria-label="第 1 章 权衡的艺术"><div class="hint"><span class="arrow left"></span>上一页</div><div class="link"><!---->第 1 章 权衡的艺术</div></a><!----></nav><!----><!----><!--]--></main><!--]--><!----><!--]--></div><!--]--><!----><!--]--></div>
    <script type="module" src="/doc/assets/app.41440b49.js" defer></script>
  </body>
</html>
